package xsql

import (
	"context"
	stdsql "database/sql"
	"go-main/library/database/sql"
)

type DBTX interface {
	DB() *sql.DB
	Tx() *sql.Tx
	IsTx() bool
}

type DbTx struct {
	db   *sql.DB
	tx   *sql.Tx
	isTx bool
}

func GetDB(db *sql.DB) *DbTx {
	return &DbTx{db: db}
}

func GetTx(t *sql.Tx) *DbTx {
	return &DbTx{tx: t, isTx: true}
}

func (d *DbTx) Tx() *sql.Tx {
	return d.tx
}

func (d *DbTx) DB() *sql.DB {
	return d.db
}

func (d *DbTx) IsTx() bool {
	return d.isTx
}

// Int64 count or select only one int field
func Int64(ctx context.Context, qu Querier, dt DBTX) (int64, error) {
	q, err := Query(ctx, qu, dt)
	if err != nil {
		return 0, err
	}
	defer q.Close()
	return ScanInt64(q)
}

// Int64 count or select only one int field
func Int64s(ctx context.Context, qu Querier, dt DBTX) ([]int64, error) {
	q, err := Query(ctx, qu, dt)
	if err != nil {
		return nil, err
	}
	defer q.Close()
	var arr []int64
	if err = ScanSlice(q, &arr); err != nil {
		return nil, err
	}
	return arr, nil
}

// Query FindRaw many record by raw sql
func String(ctx context.Context, qu Querier, dt DBTX) (string, error) {
	q, err := Query(ctx, qu, dt)
	if err != nil {
		return "", err
	}
	defer q.Close()
	return ScanString(q)
}

// Query FindRaw many record by raw sql
func Strings(ctx context.Context, qu Querier, dt DBTX) ([]string, error) {
	q, err := Query(ctx, qu, dt)
	defer q.Close()
	var arr []string
	if err = ScanSlice(q, &arr); err != nil {
		return nil, err
	}
	return arr, nil
}

// Exec
func Exec(ctx context.Context, qu Querier, dt DBTX) (stdsql.Result, error) {
	up, args := qu.Query()
	var result stdsql.Result
	var err error
	if dt.IsTx() {
		result, err = dt.Tx().Exec(up, args...)
	} else {
		result, err = dt.DB().Exec(ctx, up, args...)
	}
	if err != nil {
		return nil, err
	}
	return result, nil
}

func Query(ctx context.Context, qu Querier, dt DBTX) (*sql.Rows, error) {
	up, args := qu.Query()
	var q *sql.Rows
	var err error
	if dt.IsTx() {
		q, err = dt.Tx().Query(up, args...)
	} else {
		q, err = dt.DB().Query(ctx, up, args...)
	}
	if err != nil {
		return nil, err
	}
	return q, nil
}
